✅ AOP(Aspect Oriented Programming)은 핵심 비즈니스 로직이 아닌 공통적인 부가적인 관심사(로깅, 보안, 트랜잭션 관리 등)를 분리하여 코드의 유지보수성을 높이는 프로그래밍 기법
📌 핵심 개념
| 용어 | 설명 |
|---|---|
| Aspect | 공통 관심사(로깅, 트랜잭션, 보안 등)를 모듈화한 객체 |
| Advice | 특정 JoinPoint에서 실행될 코드 (Before, After, Around 등) |
| JoinPoint | Advice가 실행될 수 있는 지점 (메서드 실행 시점 등) |
| Pointcut | JoinPoint 중에서 Advice를 적용할 대상을 정의하는 표현식 |
| Weaving | Advice를 실제 대상 객체에 적용하는 과정 (컴파일 타임, 런타임 등) |
📌 Spring AOP의 주요 Advice 종류
✅ 오류 확률 감소와 간결한 코드를 위해 최소한의 유형 선택 권장
| Advice 유형 | 설명 | 예제 |
|---|---|---|
| Before | 대상 메서드 실행 전 실행 | @Before("execution(* com.example..*(..))") |
| After Returning | 대상 메서드 실행 후 (예외 발생 X) 실행 | @AfterReturning("execution(* com.example..*(..))") |
| After Throwing | 대상 메서드 실행 중 예외 발생 시 실행 | @AfterThrowing("execution(* com.example..*(..))") |
| After (Finally) | 대상 메서드 실행 완료 후 (성공/실패 상관없음) 실행 | @After("execution(* com.example..*(..))") |
| Around | 대상 메서드 실행 전/후 모두 실행 (가장 강력함) | @Around("execution(* com.example..*(..))") |
🔹 AOP 동작 순서
1️⃣ @Around 실행 (메서드 실행 전)
2️⃣ @Before 실행
3️⃣ 대상 메서드 실행
4️⃣ @AfterReturning 실행 (정상 종료 시)
5️⃣ @AfterThrowing 실행 (예외 발생 시)
6️⃣ @After 실행 (무조건 실행)
7️⃣ @Around 실행 (메서드 실행 후) 📌 Spring AOP 적용 방식
✅ 프록시 기반 방식과 AspectJ 기반 방식이 있다.
프록시 기반 방식
✅ Spring이 기본적으로 제공하는 AOP 방식
✅ 런타임에 프록시 객체를 생성하여 AOP 기능을 적용
✅ 스프링 컨테이너에서 관리하는 빈에만 적용 가능
🔹 동작 방식
- JDK 동적 프록시: 인터페이스가 있는 경우,
java.lang.reflect.Proxy를 이용해 프록시 생성 - CGLIB 프록시(클래스 기반): 인터페이스가 없는 경우, CGLIB 라이브러리를 사용해 바이트코드 조작으로 프록시 생성
🔹 프록시 동작 방식
AOP 없이 Spring 빈을 실행시키면, 아래와 같이 실행될 것이다.
만약 AOP를 활용하면, Spring 빈은 아래와 같이 실행된다.
POJO를 구현한 AOP 프록시 객체가 클라이언트 요청을 받아 foo() 실행 전/후 advice를 실행하는 것이다.
🔹 프록시 생성 방식
Spring AOP는 AnnotationAwareAspectJAutoProxyCreator라는 BeanProcessor의 구현체를 사용해 AOP 프록시를 생성한다.
1️⃣ Spring 컨테이너가 Spring 빈을 생성
2️⃣ BeanPostProcessor (AnnotationAwareAspectJAutoProxyCreator)가 빈을 감지
3️⃣ AOP 적용 대상이면 프록시 객체를 생성하여 빈으로 등록
4️⃣ 클라이언트가 Spring 빈을 호출하면 프록시가 먼저 실행됨
5️⃣ 프록시가 AOP Advice(Before, After 등)를 실행한 후, 원래 메서드를 실행
테스트 코드
@SpringBootTest
class MyServiceTest {
@Autowired
private MyService myService;
@Test
void testAopProxyType() {
assertThat(myService.getClass()).isEqualTo(MyService.class);
}
}결과
필요:com.example.my.MyService
실제:com.example.my.MyService$$SpringCGLIB$$0Spring AOP가 적용된 MyService의 빈 객체를 확인해보면, 프록시 객체가 감싸진 것을 확인할 수 있다.
AspectJ 기반 방식
✅ Spring AOP보다 더 강력한 AOP 방식
✅ 컴파일 타임 또는 로드 타임에 바이트코드를 수정하여 AOP 적용
✅ Spring Bean이 아닌 일반 객체에도 AOP 적용 가능
📌 예제
🔹 기본적인 Aspect 모듈
@Aspect
@Component
@Slf4j
public class LoggingAspect { // Aspect
@AfterReturning("execution(* com.example.my..*(..))") // Pointcut
public void afterMethodExecution(JoinPoint joinPoint) { // Advice
log.info("✅ service 실행 완료: ");
}
}🔹 포인트컷 분리
@Aspect
@Component
@Slf4j
public class LoggingAspect { // Aspect
@Pointcut("execution(* com.example.my..*(..))") // Pointcut
public void myMethods() {}
@AfterReturning("myMethods()")
public void afterMethodExecution(JoinPoint joinPoint) { // Advice
log.info("✅ service 실행 완료: ");
}
}📌 참고
https://docs.spring.io/spring-framework/reference/core/aop.html